/*
 * Project: guahao-portal-biz-core
 * 
 * File Created at 2012-10-10
 * 
 * Copyright 2012 Greenline.com Corporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * Greenline Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Greenline.com.
 */
package org.xukai.common.util;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xukai.common.TagsList;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Type HtmlUtils
 * @Desc html富文本处理工具类
 * @author weirui.shenwr
 * @date 2012-10-10
 * @Version V1.0
 */
public class HtmlUtils {
    private static final Logger LOG = LoggerFactory.getLogger(HtmlUtils.class);
    /**
     * html 删除替换正则
     */
    private static final Pattern html_delete_pattern = Pattern.compile("(<style\\b.*?>[\\w\\W]*?</\\s*style\\s*>)|"
            + // 过滤 style 标签
            "(<script\\b.*?>[\\w\\W]*?</\\s*script\\s*>)|"
            + // 过滤 script 标签
            "(<!--[\\w\\W]*?-->)|" + "(<[^>]+>)|" + "(&gt;)|" + "(&lt;)|" + "(&nbsp;)|" + "(&quot;)|" + "(&ldquo;)|"
            + "(&rdquo;)|" + "(&lsquo;)|" + "(&rsquo;)|" + "(&mdash;)|" + "(&hellip;)|" + "(&amp;)");

    private static final String[] replacements = { "", "", "", "", ">", "<", " ", "\"", "“", "”", "‘", "’", "—", "…",
            "&" };

    /**
     * 去除html标签，包括style，script，注释，及<xxxx>标签内内容，和控制字符
     * 
     * @param str
     * @return String
     */
    public static String deleteHtml(String str) {
        if (StringUtils.isNotBlank(str)) {
            return deleteHtml(str, false, true);
        }
        return str;
    }

    /**
     * 去除html标签，包括style，script，注释，及<xxxx>标签内内容，和控制字符 但保留转
     * 
     * @param str
     * @return String
     */
    public static String deleteHtmlS(String str) {
        if (StringUtils.isNotBlank(str)) {
            return deleteHtml(str, true, true);
        }
        return str;
    }

    /**
     * 去掉html标签，保留控制字符
     * 
     * @author wangbiao
     * @param str
     * @return String
     */
    public static String deleteHtmlKeepControlCharactor(String str) {
        if (StringUtils.isNotBlank(str)) {
            return deleteHtml(str, true, false);
        }
        return str;
    }

    /**
     * 去掉html标签
     * 
     * @param str
     * @param isKeepSearchTag 是否保留search标签
     * @param isDeleteControlCharacter 是否删除控制字符
     * @return String
     */
    private static String deleteHtml(String str, boolean isKeepSearchTag, boolean isDeleteControlCharacter) {
        Matcher m = html_delete_pattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        // 使用find()方法查找第一个匹配的对象
        boolean result = m.find();
        // 使用循环将句子里所有的kelvin找出并替换再将内容加到sb里
        while (result) {
            for (int j = 1; j < m.groupCount(); j++) {
                if (m.group(j) != null) {
                    if (isKeepSearchTag) {
                        if (m.group(j).indexOf("<em") == -1 && m.group(j).indexOf("</em>") == -1) {
                            // 排除搜索高亮
                            m.appendReplacement(sb, replacements[j - 1]);
                        }
                    } else {
                        m.appendReplacement(sb, replacements[j - 1]);
                    }
                }
            }
            // 继续查找下一个匹配对象
            result = m.find();
        }
        // 最后调用appendTail()方法将最后一次匹配后的剩余字符串加到sb里；
        m.appendTail(sb);
        str = sb.toString();
        if (isDeleteControlCharacter) {
            // 删除控制符
            str = escapeControlCharacter(str);
        }

        // &amp;符号程序无法匹配到,不知道为什么，需再处理
        str = trim(str).replaceAll("&amp;", "&");
        return str;
    }

    /**
     * 将特殊字符还原为html标签。
     * 
     * @param str
     * @return
     */
    public static final String restoreEscapedChar(String str) {
        str = str.replaceAll("&gt;", ">");
        str = str.replaceAll("&lt;", "<");
        str = str.replaceAll("&nbsp;", " ");
        str = str.replaceAll("&quot;", "\"");
        str = str.replaceAll("&ldquo;", "“");
        str = str.replaceAll("&rdquo;", "”");
        str = str.replaceAll("&lsquo;", "‘");
        str = str.replaceAll("&rsquo;", "’");
        str = str.replaceAll("&mdash;", "—");
        str = str.replaceAll("&amp;", "&"); // 必须放在最后替换
        return str;
    }

    /**
     * 改进的版本的trim，能正确处理全角空格
     * 
     * @param s
     * @return
     */
    public static String trim(String s) {
        if (StringUtils.isBlank(s)) {
            return "";
        }
        int len = s.length();
        int st = 0;
        char[] val = s.toCharArray();
        while ((st < len) && (Character.isWhitespace(val[st]))) {
            st++;
        }
        while ((st < len) && (Character.isWhitespace(val[len - 1]))) {
            len--;
        }
        return ((st > 0) || (len < s.length())) ? s.substring(st, len) : s;
    }

    /**
     * 去掉string里面的control字符,比如"\n","\r","\t"
     * 
     * @param s
     * @return
     */
    public static String escapeControlCharacter(String s) {
        if (StringUtils.isBlank(s)) {
            return "";
        }
        int len = s.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = s.charAt(i);
            if (!Character.isISOControl(c)) {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 按子节长度截取字符串(支持截取带HTML代码样式的字符串)<br>
     * 如：<span>中国人发在线</span> 当截取2个字节得到的结果是：<span>中国
     * 
     * @param param
     * @param start
     * @return
     */
    public static String subStringHTML(String param, int start) {
        return subStringHTML(param, start, null);
    }

    /**
     * 取得非html标签的字符长度
     * 
     * @param str
     * @return
     */
    public static int lengCode(String str) {
        if (StringUtils.isNotBlank(str)) {
            int n = 0;
            char temp;
            for (int i = 0; i < str.length(); i++) {
                boolean isCode = false; // 是不是HTML代码
                boolean isHTML = false; // 是不是HTML特殊字符,如
                temp = str.charAt(i);
                if (temp == '<') {
                    isCode = true;
                } else if (temp == '&') {
                    isHTML = true;
                } else if (temp == '>' && isCode) {
                    n = n - 1;
                    isCode = false;
                } else if (temp == ';' && isHTML) {
                    isHTML = false;
                }
                if (!isCode && !isHTML) {
                    n = n + 1;
                }
            }
            return n;
        }
        return 0;
    }

    /**
     * 按子节长度截取字符串(支持截取带HTML代码样式的字符串)<br>
     * 如：<span>中国人发在线</span> 当截取2个字节得到的结果是：<span>中国
     * 
     * @param param 将要截取的含html代码的字符串参数
     * @param start
     * @param end
     * @return返回截取后的字符串
     */
    public static String subStringHTML(String param, int start, Integer end) {
        if (StringUtils.isNotBlank(param)) {
            // 捕获异常，便于日志分析
            try {
                StringBuffer result = new StringBuffer();
                int n = 0;
                char temp;
                boolean isCode = false; // 是不是HTML代码
                boolean isHTML = false; // 是不是HTML特殊字符,如
                for (int i = 0; i < param.length(); i++) {
                    temp = param.charAt(i);
                    if (temp == '<') {
                        isCode = true;
                    } else if (temp == '&') {
                        isHTML = true;
                    } else if (temp == '>' && isCode) {
                        n = n - 1;
                        isCode = false;
                    } else if (temp == ';' && isHTML) {
                        isHTML = false;
                    }
                    if (!isCode && !isHTML) {
                        n = n + 1;
                    }
                    if (null != end && n >= end) {
                        break;
                    }
                    if (null == end) {
                        if (n >= start) {
                            result.append(temp);
                        }
                    } else if (n == 0 || n >= start) {
                        result.append(temp);
                    }
                }
                if (n > 0) {
                    return fix(result.toString()).trim().replaceAll("<br>", "").replaceAll("\n", "");
                }
            } catch (Exception e) {
                LOG.error("_________subStringHTML出错，内容：" + param, e);
            }
        }
        return null;
    }

    /**
     * 补全HTML代码<br>
     * 如：<span>中国 ---> <span>中国</span>
     * 
     * @param str
     * @return
     */
    private static String fix(String str) {
        StringBuffer fixed = new StringBuffer(); // 存放修复后的字符串
        TagsList[] unclosedTags = getUnclosedTags(str);
        // 生成新字符串
        for (int i = unclosedTags[0].size() - 1; i > -1; i--) {
            fixed.append("<" + unclosedTags[0].get(i) + ">");
        }
        fixed.append(str);
        for (int i = unclosedTags[1].size() - 1; i > -1; i--) {
            String s = null;
            if ((s = unclosedTags[1].get(i)) != null) {
                fixed.append("</" + s + ">");
            }
        }
        return fixed.toString();
    }

    private static TagsList[] getUnclosedTags(String str) {
        StringBuffer temp = new StringBuffer(); // 存放标签
        TagsList[] unclosedTags = new TagsList[2];
        unclosedTags[0] = new TagsList(); // 前不闭合，如有</div>而前面没有<div>
        unclosedTags[1] = new TagsList(); // 后不闭合，如有<div>而后面没有</div>
        boolean flag = false; // 记录双引号"或单引号'
        char currentJump = ' '; // 记录需要跳过''还是""
        char current = ' ', last = ' '; // 当前 & 上一个
        // 开始判断
        int strlength = str.length();
        for (int i = 0; i < strlength;) {
            current = str.charAt(i++); // 读取一个字符
            if (current == '"' || current == '/') {
                flag = flag ? false : true; // 若为引号，flag翻转
                currentJump = current;
            }
            if (!flag) {
                if (current == '<') { // 开始提取标签
                    current = str.charAt(i++);
                    if (current == '/') { // 标签的闭合部分，如</div>
                        current = str.charAt(i++);
                        // 读取标签
                        while (i < strlength && current != '>') {
                            temp.append(current);
                            current = str.charAt(i++);
                        }
                        // 从tags_bottom移除一个闭合的标签
                        if (!unclosedTags[1].remove(temp.toString())) { // 若移除失败，说明前面没有需要闭合的标签
                            unclosedTags[0].add(temp.toString()); // 此标签需要前闭合
                        }
                        temp.delete(0, temp.length()); // 清空temp
                    } else { // 标签的前部分，如<div>
                        last = current;
                        while (i < strlength && current != ' ' && current != '>') {
                            temp.append(current);
                            last = current;
                            current = str.charAt(i++);
                        }
                        // 已经读取到标签，跳过其他内容，如<div id=test>跳过id=test
                        while (i < strlength && current != '>') {
                            last = current;
                            current = str.charAt(i++);
                            if (current == '"' || current == '/') { // 判断引号
                                flag = flag ? false : true;
                                currentJump = current;
                                if (flag) { // 若引号不闭合，跳过到下一个引号之间的内容
                                    while (i < strlength && str.charAt(i++) != currentJump)
                                        ;
                                    if (i < strlength) {
                                        current = str.charAt(i++);
                                    }
                                    flag = false;
                                }
                            }
                        }
                        if (last != '/' && current == '>') // 判断这种类型：<TagName />
                            unclosedTags[1].add(temp.toString());
                        temp.delete(0, temp.length());
                    }
                }
            } else {
                while (i < strlength && str.charAt(i++) != currentJump)
                    ; // 跳过引号之间的部分
                flag = false;
            }
        }
        return unclosedTags;
    }

    /**
     * 预约规则处理
     * 
     * @param rule
     * @return
     */
    public static List<String> expertRuleHtmlProcess(String htmls) {
        List<String> liststr = new ArrayList<String>(0);
        if (StringUtils.isNotBlank(htmls)) {
            String[] strs = htmls.replaceAll("<br>", "\n").replaceAll("</br>", "\n").split("\n");
            for (String str : strs) {
                if (StringUtils.isNotBlank(str)) {
                    String s = str.trim();
                    liststr.add(s);
                }
            }
        }
        return liststr;
    }

    /**
     * 预约规则处理
     * 
     * @param rule
     * @return
     */
    public static int getRuleCount(String htmls) {
        int ruleCount = 0;
        if (StringUtils.isNotBlank(htmls)) {
            String[] strs = htmls.replaceAll("<br>", "\n").replaceAll("</br>", "\n").split("\n");
            for (String str : strs) {
                if (StringUtils.isNotBlank(str)) {
                    String s = str.trim();
                    ruleCount = ruleCount + s.length();
                }
            }
        }
        return ruleCount;
    }
}
